package com.huanxing.cloud.order.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.huanxing.cloud.common.core.constant.CommonConstants;
import com.huanxing.cloud.common.core.enums.MallErrorCodeEnum;
import com.huanxing.cloud.common.core.util.Result;
import com.huanxing.cloud.common.core.util.SnowflakeIdUtils;
import com.huanxing.cloud.common.logistics.util.Kuaidi100Utils;
import com.huanxing.cloud.common.security.handler.HxBusinessException;
import com.huanxing.cloud.common.security.util.SecurityUtils;
import com.huanxing.cloud.order.api.constant.MallOrderConstants;
import com.huanxing.cloud.order.api.dto.*;
import com.huanxing.cloud.order.api.entity.MallConfigProperties;
import com.huanxing.cloud.order.api.entity.OrderInfo;
import com.huanxing.cloud.order.api.entity.OrderItem;
import com.huanxing.cloud.order.api.entity.OrderLogistics;
import com.huanxing.cloud.order.api.enums.OrderLogisticsStateEnum;
import com.huanxing.cloud.order.api.enums.OrderStatusEnum;
import com.huanxing.cloud.order.event.HxOrderCreateAfterEvent;
import com.huanxing.cloud.order.event.HxOrderPayEvent;
import com.huanxing.cloud.order.mapper.OrderInfoMapper;
import com.huanxing.cloud.order.mapper.OrderItemMapper;
import com.huanxing.cloud.order.mapper.OrderLogisticsMapper;
import com.huanxing.cloud.order.service.IOrderInfoService;
import com.huanxing.cloud.order.service.IOrderItemService;
import com.huanxing.cloud.order.service.IOrderLogisticsService;
import com.huanxing.cloud.pay.api.constants.PayConstants;
import com.huanxing.cloud.pay.api.dto.CreateOrderReqDTO;
import com.huanxing.cloud.pay.api.remote.RemotePayService;
import com.huanxing.cloud.product.api.dto.GoodsSkuStockReqDTO;
import com.huanxing.cloud.product.api.entity.*;
import com.huanxing.cloud.product.api.remote.RemoteFreightTemplateService;
import com.huanxing.cloud.product.api.remote.RemoteGoodsAppraiseService;
import com.huanxing.cloud.product.api.remote.RemoteGoodsSkuService;
import com.huanxing.cloud.promotion.api.constant.MallEventConstants;
import com.huanxing.cloud.promotion.api.dto.CouponUserReqDTO;
import com.huanxing.cloud.promotion.api.entity.CouponGoods;
import com.huanxing.cloud.promotion.api.entity.CouponInfo;
import com.huanxing.cloud.promotion.api.enums.CouponUserStatusEnum;
import com.huanxing.cloud.promotion.api.remote.RemoteCouponUserService;
import com.huanxing.cloud.promotion.api.vo.CouponUserRespVO;
import com.huanxing.cloud.user.api.remote.RemoteMallUserService;
import com.huanxing.cloud.user.api.vo.UserInfoVO;
import lombok.RequiredArgsConstructor;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.seata.spring.annotation.GlobalTransactional;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * 订单
 *
 * @author lijx
 * @since 2022/3/7 14:18
 */
@Service
@RequiredArgsConstructor
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements IOrderInfoService {

	private final OrderLogisticsMapper orderLogisticsMapper;

	private final MallConfigProperties mallConfigProperties;

	private final Kuaidi100Utils kuaidi100Utils;

	private final OrderItemMapper orderItemMapper;

	@DubboReference
	private final RemoteGoodsSkuService remoteGoodsSkuService;

	@DubboReference
	private final RemoteMallUserService remoteMallUserService;

	private final IOrderLogisticsService orderLogisticsService;

	private final IOrderItemService orderItemService;

	@DubboReference
	private final RemotePayService remotePayService;

	@DubboReference
	private final RemoteFreightTemplateService remoteFreightTemplateService;

	private final ApplicationEventPublisher applicationEventPublisher;
	@DubboReference
	private final RemoteGoodsAppraiseService remoteGoodsAppraiseService;

	@DubboReference
	private final RemoteCouponUserService remoteCouponUserService;

	private final ThreadPoolTaskExecutor threadPoolExecutor;

	@Override
	public IPage<OrderInfo> adminPage(Page page, OrderInfo orderInfo) {
		return baseMapper.selectAdminPage(page, orderInfo);
	}

	@Override
	public OrderInfo getOrderById(String id) {
		return baseMapper.selectOrderById(id);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean deliverOrder(OrderInfo orderInfo) {
		orderInfo.setDeliverTime(LocalDateTime.now());
		orderInfo.setStatus(OrderStatusEnum.STATUS_3.getCode());
		// 查询物流信息
		OrderLogistics orderLogistics = orderLogisticsMapper.selectById(orderInfo.getOrderLogisticsId());
		if (ObjectUtil.isNull(orderLogistics)) {
			throw new HxBusinessException(MallErrorCodeEnum.ERROR_60021.getMsg());
		}
		if (StrUtil.isAllEmpty(mallConfigProperties.getLogisticsKey(), mallConfigProperties.getNotifyDomain())) {
			throw new HxBusinessException(MallErrorCodeEnum.ERROR_90001.getMsg());
		}
		orderLogistics.setLogisticsNo(orderInfo.getLogisticsNo());
		orderLogistics.setLogisticsCode(orderInfo.getLogisticsCode());
		orderLogistics.setLogisticsName(orderInfo.getLogisticsName());
		orderLogistics.setState(OrderLogisticsStateEnum.STATUS_1.getCode());
		orderLogisticsMapper.updateById(orderLogistics);

		String logisticsUrl = String.format(MallOrderConstants.NOTIFY_LOGISTICS_URL, orderLogistics.getId(),
				orderLogistics.getTenantId());

		kuaidi100Utils.poll(orderLogistics.getLogisticsCode(), orderLogistics.getLogisticsNo(),
				orderLogistics.getDetailAddress().substring(0, orderLogistics.getDetailAddress().indexOf("-")),
				mallConfigProperties.getLogisticsKey(), mallConfigProperties.getNotifyDomain() + logisticsUrl,
				orderLogistics.getTelephone());

		return baseMapper.updateById(orderInfo) > 0;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public String cancelOrder(OrderInfo orderInfo) {
		if (OrderStatusEnum.STATUS_1.getCode().equals(orderInfo.getStatus())) {
			orderInfo.setStatus(OrderStatusEnum.STATUS_11.getCode());
			orderInfo.setCancelTime(LocalDateTime.now());
			baseMapper.updateById(orderInfo);

			// 回滚优惠券
			if (StringUtils.hasText(orderInfo.getCouponUserId())) {
				CouponUserReqDTO couponUserReqDTO = new CouponUserReqDTO();
				couponUserReqDTO.setId(orderInfo.getCouponUserId());
				couponUserReqDTO.setCouponUserStatusEnum(CouponUserStatusEnum.STATUS_0);
				remoteCouponUserService.updateCouponUserStatus(couponUserReqDTO);
			}

			List<OrderItem> orderItemList = orderItemMapper
				.selectList(Wrappers.<OrderItem>lambdaQuery().eq(OrderItem::getOrderId, orderInfo.getId()));
			List<GoodsSkuStockReqDTO> goodsSkuStockRqDTOList = orderItemList.stream().map(orderItem -> {
				GoodsSkuStockReqDTO goodsSkuStockRqDTO = new GoodsSkuStockReqDTO();
				goodsSkuStockRqDTO.setStockNum(orderItem.getBuyQuantity());
				goodsSkuStockRqDTO.setSkuId(orderItem.getSkuId());
				return goodsSkuStockRqDTO;
			}).collect(Collectors.toList());
			// 回滚库存
			remoteGoodsSkuService.rollbackStock(goodsSkuStockRqDTOList);
		}
		return orderInfo.getId();
	}

	@Override
	public BigDecimal getPaySumStatistics(OrderInfoDTO orderInfoDTO) {
		return baseMapper.selectPaySumStatistics(orderInfoDTO);
	}

	@Override
	public IPage<OrderInfo> apiPage(Page page, OrderInfo orderInfo) {
		return baseMapper.selectApiPage(page, orderInfo);
	}

	@Override
	@GlobalTransactional
	public OrderInfo createOrder(CreateOrderDTO createOrderDTO) {
		// 查询用户信息
		UserInfoVO userInfo = remoteMallUserService.getUserById(createOrderDTO.getUserId());
		if (Objects.isNull(userInfo)) {
			throw new HxBusinessException(MallErrorCodeEnum.ERROR_50001.getMsg());
		}

			List<String> skuIds = createOrderDTO.getSkuReqList()
				.stream()
				.map(CreateOrderSkuReqDTO::getSkuId)
				.collect(Collectors.toList());
			// 查询购买商品
			List<GoodsSku> goodsSkuList = remoteGoodsSkuService.getBySkuIds(skuIds);
			if (CollectionUtils.isEmpty(goodsSkuList)) {
				throw new HxBusinessException(MallErrorCodeEnum.ERROR_60008.getCode(),
						MallErrorCodeEnum.ERROR_60008.getMsg());
			}

			// 生成订单
			OrderInfo orderInfo = generateOrder(createOrderDTO);
			// 生成订单商品
			List<OrderItem> orderItemList = generateOrderItems(goodsSkuList, createOrderDTO.getSkuReqList());

			// 创建订单前 Before
			computeOrderPrice(orderInfo, orderItemList);
			// 1. 校验库存扣减库存
			orderStockHandler(goodsSkuList, orderItemList);
			// 2. 计算运费
			orderFreightHandler(orderInfo, orderItemList, goodsSkuList);
			// 3. 计算优惠券优惠金额
			orderCouponHandler(orderInfo, orderItemList);
			// 4. 物流配送
			if (MallOrderConstants.DELIVERY_WAY_1.equals(orderInfo.getDeliveryWay())) {
				OrderLogistics orderLogistics = orderLogisticsService
					.generateOrderLogistics(createOrderDTO.getUserAddressId());
				orderLogisticsService.save(orderLogistics);
				// 保存物流信息
				orderInfo.setOrderLogisticsId(orderLogistics.getId());
			}

			// 创建订单
			if (!super.save(orderInfo)) {
				throw new HxBusinessException(MallErrorCodeEnum.ERROR_60007.getCode(),
						MallErrorCodeEnum.ERROR_60007.getMsg());
			}
			orderItemList.forEach(orderItem -> orderItem.setOrderId(orderInfo.getId()));
			orderItemService.saveBatch(orderItemList);
			// 创建订单后
			// 1. 清除购物车 2. 发送延迟取消订单mq消息 3. 发送微信小程序模板消息
			applicationEventPublisher.publishEvent(
					new HxOrderCreateAfterEvent(this, orderInfo, orderItemList, createOrderDTO.getCreateWay()));

		return orderInfo;
	}

	private void orderFreightHandler(OrderInfo orderInfo, List<OrderItem> orderItemList, List<GoodsSku> goodsSkuList) {
		if (!CommonConstants.YES.equals(orderInfo.getDeliveryWay())) {
			return;
		}
		Map<String, BigDecimal> freightTotalCountMap = new HashMap<>();
		for (OrderItem orderItem : orderItemList) {

			GoodsSku goodsSku = goodsSkuList.stream()
				.filter(v -> v.getId().equals(orderItem.getSkuId()))
				.findFirst()
				.get();

			String freightTemplateId = goodsSku.getGoodsSpu().getFreightTemplateId();
			BigDecimal freightPrice = BigDecimal.ZERO;
			// 查询运费模板
			FreightTemplate freightTemplate = remoteFreightTemplateService.getById(freightTemplateId);
			if (Objects.isNull(freightTemplate)) {
				throw new IllegalArgumentException("get freight template failed!");
			}
			// 查询map里模板 >0说明已有相同模板的商品计算过
			BigDecimal count = freightTotalCountMap.getOrDefault(freightTemplate.getId(), BigDecimal.ZERO);

			// 买家承担运费
			if (CommonConstants.NO.equals(freightTemplate.getIsInclPostage())) {
				// 判断计算方式
				switch (freightTemplate.getPricingType()) {
					case MallOrderConstants.PRICING_TYPE_1 -> {
						// 按件数收费
						// 累加购买数量
						BigDecimal quantity = BigDecimal.valueOf(orderItem.getBuyQuantity()).add(count);
						// 添加到map
						freightTotalCountMap.put(freightTemplate.getId(), quantity);
						freightPrice = this.freightCompute(freightTemplate, quantity, count);
					}
					case MallOrderConstants.PRICING_TYPE_2 -> {
						BigDecimal weight = goodsSku.getWeight()
							.multiply(BigDecimal.valueOf(orderItem.getBuyQuantity()))
							.add(count);
						// 添加到map
						freightTotalCountMap.put(freightTemplate.getId(), weight);
						freightPrice = this.freightCompute(freightTemplate, weight, count);
					}
					// 按重量收费
					default -> throw new HxBusinessException("运费模板错误");
				}
			}
			orderItem.setFreightPrice(freightPrice);
			computeItemPayPrice(orderItem);
		}
		computeOrderPrice(orderInfo, orderItemList);
	}

	public BigDecimal freightCompute(FreightTemplate freightTemplate, BigDecimal quantity, BigDecimal count) {
		BigDecimal freightPrice;
		if (quantity.compareTo(freightTemplate.getFirstNum()) <= 0) {
			// 首件商品
			freightPrice = freightTemplate.getFirstFreight();
			// 同商品模板已计算
			if (count.compareTo(BigDecimal.ZERO) <= 0) {
				freightPrice = freightTemplate.getFirstFreight();
			}
		}
		else {
			// 续件数量
			BigDecimal continueCount = quantity.subtract(freightTemplate.getFirstNum());
			if (count.compareTo(freightTemplate.getFirstNum()) >= 0) {
				// 计算首件
				continueCount = quantity.subtract(count);
			}
			// 续件数量倍数
			BigDecimal continueRate = continueCount.divide(freightTemplate.getContinueNum(), 0);
			// 续件价格
			BigDecimal continueFreight = continueRate.multiply(freightTemplate.getContinueFreight());
			// 总价格
			if (count.compareTo(freightTemplate.getFirstNum()) >= 0) {
				freightPrice = continueFreight;
			}
			else {
				freightPrice = continueFreight.add(freightTemplate.getFirstFreight());
			}
		}

		return freightPrice;
	}

	private void orderStockHandler(List<GoodsSku> goodsSkuList, List<OrderItem> orderItemList) {
		List<GoodsSku> skuList = goodsSkuList.stream()
			.filter(goodsSku -> goodsSku.getStock() >= orderItemList.stream()
				.filter(skuReq -> skuReq.getSkuId().equals(goodsSku.getId()))
				.findFirst()
				.get()
				.getBuyQuantity())
			.toList();
		if (CollUtil.isEmpty(skuList) || skuList.size() < orderItemList.size()) {
			throw new HxBusinessException(MallErrorCodeEnum.ERROR_60008.getCode(),
					MallErrorCodeEnum.ERROR_60008.getMsg());
		}
		// 扣减库存
		boolean result = remoteGoodsSkuService.reduceStock(orderItemList.stream().map(v -> {
			GoodsSkuStockReqDTO goodsSkuStockReqDTO = new GoodsSkuStockReqDTO();
			goodsSkuStockReqDTO.setSkuId(v.getSkuId());
			goodsSkuStockReqDTO.setStockNum(v.getBuyQuantity());
			return goodsSkuStockReqDTO;
		}).toList());
		if (!result) {
			throw new HxBusinessException(MallErrorCodeEnum.ERROR_60008.getCode(),
					MallErrorCodeEnum.ERROR_60008.getMsg());
		}
	}

	// 计算订单金额
	private void computeOrderPrice(OrderInfo orderInfo, List<OrderItem> orderItemList) {
		BigDecimal totalPrice = BigDecimal.ZERO;
		BigDecimal paymentPrice = BigDecimal.ZERO;
		BigDecimal freightPrice = BigDecimal.ZERO;
		BigDecimal couponPrice = BigDecimal.ZERO;

		for (OrderItem orderItem : orderItemList) {
			totalPrice = totalPrice.add(orderItem.getTotalPrice());
			freightPrice = freightPrice.add(orderItem.getFreightPrice());
			couponPrice = couponPrice.add(orderItem.getCouponPrice());
			paymentPrice = paymentPrice.add(orderItem.getPaymentPrice());
		}

		orderInfo.setTotalPrice(totalPrice)
			.setPaymentPrice(paymentPrice)
			.setFreightPrice(freightPrice)
			.setCouponPrice(couponPrice);
	}

	// 计算订单商品支付金额
	private void computeItemPayPrice(OrderItem orderItem) {
		orderItem.setPaymentPrice(
				orderItem.getTotalPrice().subtract(orderItem.getCouponPrice()).add(orderItem.getFreightPrice()));
	}

	/**
	 * 使用优惠券
	 * @param orderInfo
	 * @param orderItemList
	 */
	private void orderCouponHandler(OrderInfo orderInfo, List<OrderItem> orderItemList) {
		if (!StringUtils.hasText(orderInfo.getCouponUserId())) {
			return;
		}
		// 优惠券优惠金额
		BigDecimal couponTotalAmount = BigDecimal.ZERO;
		// 可使用优惠券的商品总金额
		BigDecimal totalPrice = BigDecimal.ZERO;
		// 优惠券可使用指定商品
		List<OrderItem> listCouponGoods = null;
		// 优惠券可用范围
		String couponUseRange = MallEventConstants.USE_RANGE_1;
		// 校验优惠券
		CouponUserRespVO couponUserRespVO = remoteCouponUserService.getById(orderInfo.getCouponUserId());
		if (Objects.isNull(couponUserRespVO)) {
			throw new HxBusinessException(MallErrorCodeEnum.ERROR_60060.getCode(),
					MallErrorCodeEnum.ERROR_60060.getMsg());
		}
		CouponInfo couponInfo = couponUserRespVO.getCouponInfo();
		couponUseRange = couponInfo.getUseRange();
		// 指定商品
		if (MallEventConstants.USE_RANGE_2.equals(couponInfo.getUseRange())) {
			List<CouponGoods> couponGoodsList = couponUserRespVO.getCouponGoodsList();
			if (CollUtil.isEmpty(couponGoodsList)) {
				throw new HxBusinessException(MallErrorCodeEnum.ERROR_60060.getCode(),
						MallErrorCodeEnum.ERROR_60060.getMsg());
			}
			// 过滤指定商品
			listCouponGoods = orderItemList.stream()
				.map(map -> couponGoodsList.stream()
					.filter(m -> Objects.equals(m.getSpuId(), map.getSpuId()))
					.findFirst()
					.map(m -> {
						return map;
					})
					.orElse(null))
				.filter(Objects::nonNull)
				.collect(Collectors.toList());
			// 验证优惠券 并计算 可使用优惠券的订单总金额
			totalPrice = this.verifyCoupon(listCouponGoods, couponUserRespVO);
		}
		else {
			// 验证优惠券 并计算 可使用优惠券的订单总金额
			totalPrice = this.verifyCoupon(orderItemList, couponUserRespVO);
		}
		// 计算优惠券优惠金额
		couponTotalAmount = this.couponCompute(totalPrice, couponUserRespVO, couponInfo);
		for (OrderItem orderItem : orderItemList) {
			// 计算优惠券
			BigDecimal couponPrice = BigDecimal.ZERO;
			if (couponTotalAmount.compareTo(BigDecimal.ZERO) > 0) {
				boolean isComputeCoupon = true;
				// 指定商品使用 校验本次循环商品是否满足
				if (MallEventConstants.USE_RANGE_2.equals(couponUseRange)) {
					isComputeCoupon = listCouponGoods.stream().anyMatch(a -> a.getSkuId().equals(orderItem.getSkuId()));
				}
				if (isComputeCoupon) {
					// 单个明细金额占比
					BigDecimal oneMoneyScope = orderItem.getTotalPrice().divide(totalPrice, 2, BigDecimal.ROUND_UP);
					// 分配优惠券金额按比例 优惠金额末位舍0进1
					couponPrice = oneMoneyScope.multiply(couponTotalAmount).setScale(2, BigDecimal.ROUND_UP);
					// 如果分配比例大于金额 则等于金额
					if (couponPrice.compareTo(orderItem.getTotalPrice()) > 0) {
						couponPrice = orderItem.getTotalPrice();
					}
					// 优惠金额去除本次优惠金额
					couponTotalAmount = couponTotalAmount.subtract(couponPrice);
					// 总金额减去本次商品金额
					totalPrice = totalPrice.subtract(orderItem.getTotalPrice());
				}
			}
			orderItem.setCouponPrice(couponPrice);
			computeItemPayPrice(orderItem);
		}
		computeOrderPrice(orderInfo, orderItemList);
	}

	private BigDecimal verifyCoupon(List<OrderItem> listCouponGoods, CouponUserRespVO couponUserRespVO) {
		List<OrderItem> orderItems = listCouponGoods;
		if (!couponUserRespVO.getStatus().equals(CouponUserStatusEnum.STATUS_0.getCode())) {
			throw new HxBusinessException(MallErrorCodeEnum.ERROR_60061.getCode(),
					MallErrorCodeEnum.ERROR_60061.getMsg());
		}
		if (couponUserRespVO.getValidatTime().isBefore(LocalDateTime.now())) {
			throw new HxBusinessException(MallErrorCodeEnum.ERROR_60060.getCode(),
					MallErrorCodeEnum.ERROR_60060.getMsg());
		}

		AtomicReference<BigDecimal> atomicTotalPrice = new AtomicReference<>(BigDecimal.ZERO);
		orderItems.forEach(orderItem -> {
			Integer quantity = listCouponGoods.stream()
				.filter(tree -> tree.getSkuId().equals(orderItem.getSkuId()))
				.toList()
				.get(0)
				.getBuyQuantity();
			atomicTotalPrice.updateAndGet(
					v -> atomicTotalPrice.get().add(orderItem.getSalesPrice().multiply(BigDecimal.valueOf(quantity))));
		});
		return atomicTotalPrice.get();
	}

	// 计算优惠券
	public BigDecimal couponCompute(BigDecimal totalPrice, CouponUserRespVO couponUser, CouponInfo couponInfo) {
		BigDecimal couponAmount = BigDecimal.ZERO;

		if (!couponUser.getStatus().equals(CouponUserStatusEnum.STATUS_0.getCode())) {
			throw new HxBusinessException(MallErrorCodeEnum.ERROR_60061.getCode(),
					MallErrorCodeEnum.ERROR_60061.getMsg());
		}
		if (couponUser.getValidatTime().isBefore(LocalDateTime.now())) {
			throw new HxBusinessException(MallErrorCodeEnum.ERROR_60060.getCode(),
					MallErrorCodeEnum.ERROR_60060.getMsg());
		}

		if (couponInfo.getThreshold().compareTo(BigDecimal.ZERO) > 0
				&& totalPrice.compareTo(couponInfo.getThreshold()) < 0) {
			throw new HxBusinessException(MallErrorCodeEnum.ERROR_60060.getCode(),
					MallErrorCodeEnum.ERROR_60060.getMsg());
		}

		if (MallEventConstants.COUPON_TYPE_1.equals(couponInfo.getCouponType())) {
			// 满减
			couponAmount = couponInfo.getAmount();
		}
		else if (MallEventConstants.COUPON_TYPE_2.equals(couponInfo.getCouponType())) {
			// 折扣
			BigDecimal discount = couponInfo.getDiscount().divide(BigDecimal.valueOf(10), 2);
			// 优惠后金额
			couponAmount = totalPrice.multiply(discount);
		}
		couponUser.setStatus(CouponUserStatusEnum.STATUS_3.getCode());
		return couponAmount;
	}

	private List<OrderItem> generateOrderItems(List<GoodsSku> goodsSkuList, List<CreateOrderSkuReqDTO> skuReqList) {
		return goodsSkuList.stream().map(sku -> {

			CreateOrderSkuReqDTO placeOrderSku = skuReqList.stream()
				.filter(tree -> tree.getSkuId().equals(sku.getId()))
				.toList()
				.get(0);
			GoodsSpu goodsSpu = sku.getGoodsSpu();
			OrderItem orderItem = new OrderItem();
			orderItem.setIsRefund(CommonConstants.NO);
			orderItem.setBuyQuantity(placeOrderSku.getQuantity());
			orderItem.setSpuName(goodsSpu.getName());
			orderItem.setSpuId(goodsSpu.getId());
			orderItem.setSkuId(sku.getId());
			orderItem.setPicUrl(goodsSpu.getSpuUrls().get(0));
			orderItem.setSalesPrice(sku.getSalesPrice());
			orderItem.setTotalPrice(sku.getSalesPrice().multiply(BigDecimal.valueOf(placeOrderSku.getQuantity())));
			orderItem.setFreightPrice(BigDecimal.ZERO);
			orderItem.setCouponPrice(BigDecimal.ZERO);
			orderItem.setPaymentPrice(orderItem.getTotalPrice());
			// TODO 是否改为前端传？？
			List<GoodsSkuSpecsValue> goodsSkuSpecsValues = remoteGoodsSkuService
				.selectGoodsSkuSpecsValueList(sku.getId());
			if (!CollectionUtils.isEmpty(goodsSkuSpecsValues)) {
				orderItem.setSpecsInfo(goodsSkuSpecsValues.stream()
					.map(GoodsSkuSpecsValue::getSpecsValueName)
					.collect(Collectors.joining("；")));
				orderItem.setPicUrl(!StringUtils.hasText(goodsSkuSpecsValues.get(0).getPicUrl())
						? goodsSpu.getSpuUrls().get(0) : goodsSkuSpecsValues.get(0).getPicUrl());
			}
			return orderItem;
		}).collect(Collectors.toList());

	}

	private OrderInfo generateOrder(CreateOrderDTO createOrderDTO) {
		OrderInfo orderInfo = new OrderInfo();
		BeanUtil.copyProperties(createOrderDTO, orderInfo);
		orderInfo.setAppraiseStatus(CommonConstants.NO);
		orderInfo.setOrderNo(SnowflakeIdUtils.orderNo());
		orderInfo.setPaymentPrice(BigDecimal.ZERO);
		orderInfo.setStatus(OrderStatusEnum.STATUS_1.getCode());
		orderInfo.setTotalPrice(BigDecimal.ZERO);
		orderInfo.setFreightPrice(BigDecimal.ZERO);
		orderInfo.setCouponPrice(BigDecimal.ZERO);
		orderInfo.setPayStatus(CommonConstants.NO);
		orderInfo.setCouponUserId(createOrderDTO.getCouponUserId());
		return orderInfo;
	}

	@Override
	public boolean receiveOrder(OrderInfo orderInfo) {
		orderInfo.setReceiverTime(LocalDateTime.now());
		orderInfo.setStatus(OrderStatusEnum.STATUS_4.getCode());
		baseMapper.updateById(orderInfo);
		return Boolean.TRUE;
	}

	@Override
	public Result<Object> prepay(PrepayDTO prepayDTO) {
		String tradeType = prepayDTO.getTradeType();
		String paymentType = prepayDTO.getPaymentType();
		String returnUrl = prepayDTO.getReturnUrl();
		String quitUrl = prepayDTO.getQuitUrl();
		if (StrUtil.isBlank(tradeType)) {
			return Result.fail(MallErrorCodeEnum.ERROR_60001.getCode(), MallErrorCodeEnum.ERROR_60001.getMsg());
		}
		if (StrUtil.isBlank(paymentType)) {
			return Result.fail(MallErrorCodeEnum.ERROR_60002.getCode(), MallErrorCodeEnum.ERROR_60002.getMsg());
		}
		OrderInfo orderInfo = this.getById(prepayDTO.getId());
		if (ObjectUtil.isNull(orderInfo)) {
			return Result.fail(MallErrorCodeEnum.ERROR_60003.getCode(), MallErrorCodeEnum.ERROR_60003.getMsg());
		}
		// 只有未支付的详单能发起支付
		if (CommonConstants.YES.equals(orderInfo.getPayStatus())) {
			return Result.fail(MallErrorCodeEnum.ERROR_60004.getCode(), MallErrorCodeEnum.ERROR_60004.getMsg());
		}
		// 0元支付
		if (orderInfo.getPaymentPrice().compareTo(BigDecimal.ZERO) == 0) {
			orderInfo.setPaymentTime(LocalDateTime.now());
			orderInfo.setPaymentType(CommonConstants.NO);
			applicationEventPublisher.publishEvent(new HxOrderPayEvent(this, orderInfo, orderItemService
				.list(Wrappers.<OrderItem>lambdaQuery().eq(OrderItem::getOrderId, orderInfo.getId()))));
			return Result.success();
		}
		if (StrUtil.isBlank(mallConfigProperties.getNotifyDomain())) {
			return Result.fail(MallErrorCodeEnum.ERROR_90001.getCode(), MallErrorCodeEnum.ERROR_90001.getMsg());
		}
		String body = "商城购物";
		CreateOrderReqDTO createOrderReqDTO = new CreateOrderReqDTO();
		createOrderReqDTO.setTradeType(tradeType);
		createOrderReqDTO.setSubject(body);
		createOrderReqDTO.setBuyerId(SecurityUtils.getUser().getOpenId());
		createOrderReqDTO.setTotalAmount(orderInfo.getPaymentPrice().toString());
		createOrderReqDTO.setNotifyUrl(mallConfigProperties.getNotifyDomain());
		createOrderReqDTO.setOutTradeNo(orderInfo.getOrderNo());
		createOrderReqDTO.setQuitUrl(quitUrl);
		createOrderReqDTO.setReturnUrl(returnUrl);
		JSONObject extraParams = new JSONObject();
		extraParams.put(PayConstants.EXTRA_PARAMS_PAY_TYPE, paymentType);
		createOrderReqDTO.setExtra(extraParams.toJSONString());

		return Result.success(remotePayService.createOrder(createOrderReqDTO));
	}

	@Override
	public boolean appraiseOrder(String id, List<OrderAppraiseDTO> orderAppraiseList) {

		String userId = SecurityUtils.getUser().getUserId();
		// 查询订单
		OrderInfo orderInfo = this.getById(id);
		if (Objects.isNull(orderInfo)) {
			return false;
		}
		if (!orderInfo.getAppraiseStatus().equals(CommonConstants.NO)) {
			throw new HxBusinessException("订单已评价");
		}
		// 查询用户信息
		UserInfoVO userInfo = remoteMallUserService.getUserById(userId);
		if (Objects.isNull(userInfo)) {
			throw new HxBusinessException(MallErrorCodeEnum.ERROR_50001.getMsg());
		}

		List<GoodsAppraise> goodsAppraiseList = orderAppraiseList.stream().map(v -> {
			GoodsAppraise goodsAppraise = new GoodsAppraise();
			BeanUtil.copyProperties(v, goodsAppraise);
			goodsAppraise.setUserId(userId);
			goodsAppraise.setAvatarUrl(userInfo.getAvatarUrl());
			goodsAppraise.setNickName(userInfo.getNickName());
			return goodsAppraise;
		}).collect(Collectors.toList());

		if (!remoteGoodsAppraiseService.addGoodsAppraise(goodsAppraiseList)) {
			throw new HxBusinessException("订单评价失败");
		}

		// 修改订单评价状态
		orderInfo.setAppraiseStatus(CommonConstants.YES);
		return this.updateById(orderInfo);
	}

	@Override
	public List<Map<String, Object>> statistics() {
		List<Map<String, Object>> reList = new ArrayList<>();
		// 当前时间
		LocalDateTime now = LocalDateTime.now();
		for (int i = 0; i < 11; i++) {
			LocalDateTime offset = LocalDateTimeUtil.offset(now, -i, ChronoUnit.MONTHS);
			int year = offset.getYear();
			Month month = offset.getMonth();
			LocalDateTime startOfMonth = LocalDateTime.of(year, month, 1, 0, 0, 0);
			LocalDateTime endOfMonth = startOfMonth.withDayOfMonth(month.length(false))
				.withHour(23)
				.withMinute(59)
				.withSecond(59);
			Map<String, Object> rtMap = new HashMap<>();
			OrderInfoDTO orderInfoDTO = new OrderInfoDTO();
			orderInfoDTO.setPayStatus(CommonConstants.YES);
			orderInfoDTO.setPaymentType(MallOrderConstants.PAYMENT_TYPE_1);
			orderInfoDTO.setBeginTime(startOfMonth);
			orderInfoDTO.setEndTime(endOfMonth);
			BigDecimal wxPaySum = baseMapper.selectPaySumStatistics(orderInfoDTO);

			orderInfoDTO.setPaymentType(MallOrderConstants.PAYMENT_TYPE_2);
			BigDecimal aliPaySum = baseMapper.selectPaySumStatistics(orderInfoDTO);
			rtMap.put("wxCount", wxPaySum);
			rtMap.put("aliCount", aliPaySum);
			rtMap.put("date", LocalDateTimeUtil.format(offset, DatePattern.NORM_MONTH_PATTERN));
			reList.add(rtMap);
		}
		return reList;
	}

	@Override
	public OrderInfo settlementOrder(SettlementOrderDTO settlementOrderDTO) {

			List<String> skuIds = settlementOrderDTO.getSkuReqList()
				.stream()
				.map(CreateOrderSkuReqDTO::getSkuId)
				.collect(Collectors.toList());
			// 查询购买商品
			List<GoodsSku> goodsSkuList = remoteGoodsSkuService.getBySkuIds(skuIds);
			if (CollectionUtils.isEmpty(goodsSkuList)) {
				throw new HxBusinessException(MallErrorCodeEnum.ERROR_60008.getCode(),
						MallErrorCodeEnum.ERROR_60008.getMsg());
			}

			// 生成订单
			OrderInfo orderInfo = new OrderInfo();
			BeanUtil.copyProperties(settlementOrderDTO, orderInfo);
			orderInfo.setTotalPrice(BigDecimal.ZERO);
			orderInfo.setFreightPrice(BigDecimal.ZERO);
			orderInfo.setCouponPrice(BigDecimal.ZERO);
			orderInfo.setPayStatus(CommonConstants.NO);
			orderInfo.setCouponUserId(settlementOrderDTO.getCouponUserId());
			// 生成订单商品
			List<OrderItem> orderItemList = generateOrderItems(goodsSkuList, settlementOrderDTO.getSkuReqList());

			// 创建订单前 Before
			computeOrderPrice(orderInfo, orderItemList);
			// 2. 计算运费
			orderFreightHandler(orderInfo, orderItemList, goodsSkuList);
			// 3. 计算优惠券优惠金额
			orderCouponHandler(orderInfo, orderItemList);
			orderInfo.setOrderItemList(orderItemList);
		return orderInfo;
	}

}
